﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace DataInterpolator.Model
{
   public class LogFile
   {
      Regex _WellRegex = new Regex(@"(?: Wellname)(?::?)(?: *)(?<name>.+)");
      Regex _TimeDatumRegex = new Regex(@"(?:Time Datum)(?::?)(?: *)(?<value>[0-9/.]+)");
      Regex _KBRegex = new Regex(@"(?:KB)(?::?)(?: *)(?<value>[0-9/.]+)");
      Regex _SampleRegex = new Regex(@"(?:Sample Rate = )(?: *)(?<rate>[0-9/.]+)");

      public LogFile(string filename)
      {
         Parsed = false;
         Filename = filename;
      }

      public int RowCount
      {
         get; set;
      }

      public bool Parsed
      {
         get; private set;
      }

      public string Filename
      {
         get; private set;
      }

      public Well Well
      {
         get; private set;
      }

      private string[] SeparateLine(string line, int[] adv)
      {
         //The first column is 10, the remainder are 15 characters wide
         if (line.Length < adv[0])
            return null;

         List<string> separated = new List<string>();

         int pos = adv[0];
         int advIdx = 0;

         while (pos <= line.Length)
         {
            separated.Add(line.Substring(pos - adv[advIdx], adv[advIdx]).Trim());

            if (advIdx < adv.Length - 1)
               advIdx++;

            pos += adv[advIdx];
         }

         return separated.ToArray();
      }

      void FindLeadTrailZeros(List<double> data, out int firstValid, out int lastValid)
      {
         firstValid = data.FindIndex((val) =>
         {
            return val != 0.0 && !double.IsNaN(val);
         });

         if (firstValid > 0 && data.Count - 1 > firstValid + 1)
         {
            //Check the rate to see if 0 is a valid value
            double rate = data[firstValid + 1] - data[firstValid];

            if (data[firstValid] - rate == 0.0)
               firstValid--;
         }

         lastValid = data.FindLastIndex((val) =>
         {
            return val != 0.0 && !double.IsNaN(val);
         });

         if (lastValid != -1 && lastValid < data.Count + 1 && data.Count - 1 > lastValid + 1)
         {
            //Check the rate to see if 0 is a valid value
            double rate = data[lastValid] - data[lastValid - 1];

            if (data[firstValid] + rate == 0.0)
               lastValid++;
         }
      }

      private int FindWellname(string[] lines, out string wellname)
      {
         wellname = "";

         for (int i = 0; i < lines.Length; i++)
         {
            Match match = _WellRegex.Match(lines[i]);

            if (match.Success)
            {
               wellname = match.Groups["name"].Value.Trim();
               return i;
            }
         }

         return -1;
      }

      private int[] GetHeaderIndexes(string[] splitHeaders)
      {
         int[] headerIdx = new int[] { -1, -1, -1, -1, -1 };

         for (int i = 0; i < splitHeaders.Length; i++)
         {
            if (headerIdx[0] == -1 && splitHeaders[i].Trim().ToLower() == "time")
            {
               headerIdx[0] = i;
            }
            else if (headerIdx[1] == -1 && splitHeaders[i].Trim().ToLower() == "depth")
            {
               headerIdx[1] = i;
            }
            else if (headerIdx[2] == -1 && splitHeaders[i].Trim().ToLower() == "dt1")
            {
               headerIdx[2] = i;
            }
            else if (headerIdx[3] == -1 && (splitHeaders[i].Trim().ToLower() == "rhob1" || splitHeaders[i].Trim().ToLower() == "rhoz1"))
            {
               headerIdx[3] = i;
            }
            else if (headerIdx[4] == -1 && splitHeaders[i].Trim().ToLower() == "(1)velocity")
            {
               headerIdx[4] = i;
            }

         }

         return headerIdx;
      }

      private void ParseLogDataFile(string[] lines, int startIndex)
      {
         Well.LogData = new Well_LogData();

         //Find the KB
         Match match = _SampleRegex.Match(lines[startIndex + 3]);

         double sampleRate = 0.0;

         if (!match.Success || !Double.TryParse(match.Groups["rate"].Value.Trim(), out sampleRate))
         {
            throw new Exception();
         }

         Well.SampleRate = sampleRate;

         int[] adv = new int[2] { 10, 15 };

         //First find if any columns are missing
         int[] headerIdx = GetHeaderIndexes(SeparateLine(lines[startIndex + 6], adv));

         if (headerIdx[0] == -1)
            throw new Exception("Missing TIME column in log file. Cannot import log files without the TIME column");

         for (int i = startIndex + 10; i < lines.Length; i++)
         {
            string[] splitData = SeparateLine(lines[i], adv);

            if (splitData.Length < 2)
               continue;

            double time = double.NaN;

            //Check for special values that indicate null values
            if (!Double.TryParse(splitData[headerIdx[0]], out time))
            {
               continue;
            }

            if (time == -9999.25)
            {
               time = double.NaN;
            }

            double depth = double.NaN;

            //Check for special values that indicate null values
            if (headerIdx[1] != -1 && !Double.TryParse(splitData[headerIdx[1]], out depth))
            {
               continue;
            }

            if (depth == -9999.25)
            {
               depth = double.NaN;
            }

            double dt1 = double.NaN;

            //Check for special values that indicate null values
            if (headerIdx[2] != -1 && !Double.TryParse(splitData[headerIdx[2]], out dt1))
            {
               continue;
            }

            if (dt1 == -9999.25)
            {
               dt1 = double.NaN;
            }

            double rhoB1 = double.NaN;

            //Check for special values that indicate null values
            if (headerIdx[3] != -1 && !Double.TryParse(splitData[headerIdx[3]], out rhoB1))
            {
               continue;
            }

            if (rhoB1 == -9999.25)
            {
               rhoB1 = double.NaN;
            }

            double velocity = double.NaN;

            //Check for special values that indicate null values
            if (headerIdx[4] != -1 && !Double.TryParse(splitData[headerIdx[4]], out velocity))
            {
               continue;
            }

            if (velocity == -9999.25)
            {
               velocity = double.NaN;
            }

            Well.LogData.Time.Add(time);
            Well.LogData.Depth.Add(depth);
            Well.LogData.DT1.Add(dt1);
            Well.LogData.RhoB1.Add(rhoB1);
            Well.LogData.Velocity.Add(velocity);
         }

         int firstValid = 0;
         int lastValid = 0;

         FindLeadTrailZeros(Well.LogData.Time, out firstValid, out lastValid);

         for (int i = 0; i < Well.LogData.Time.Count; i++)
         {
            if (i < firstValid || i > lastValid)
               Well.LogData.Time[i] = double.NaN;
         }

         FindLeadTrailZeros(Well.LogData.Depth, out firstValid, out lastValid);

         for (int i = 0; i < Well.LogData.Depth.Count; i++)
         {
            if (i < firstValid || i > lastValid)
               Well.LogData.Depth[i] = double.NaN;
         }

         FindLeadTrailZeros(Well.LogData.DT1, out firstValid, out lastValid);

         for (int i = 0; i < Well.LogData.DT1.Count; i++)
         {
            if (i < firstValid || i > lastValid)
               Well.LogData.DT1[i] = double.NaN;
         }

         FindLeadTrailZeros(Well.LogData.RhoB1, out firstValid, out lastValid);

         for (int i = 0; i < Well.LogData.RhoB1.Count; i++)
         {
            if (i < firstValid || i > lastValid)
               Well.LogData.RhoB1[i] = double.NaN;
         }

         FindLeadTrailZeros(Well.LogData.Velocity, out firstValid, out lastValid);

         for (int i = 0; i < Well.LogData.Velocity.Count; i++)
         {
            if (i < firstValid || i > lastValid)
               Well.LogData.Velocity[i] = double.NaN;
         }
      }

      private void ParseTopsFile(string[] lines, int startIndex)
      {
         Well.Tops = new Well_Tops();

         int[] adv = new int[5] { 17, 10, 15, 13, 15 };

         for (int i = startIndex + 8; i < lines.Length; i++)
         {
            string[] splitData = SeparateLine(lines[i], adv);

            if (splitData.Length < 4)
               continue;

            Well_Formation form = new Well_Formation();

            form.Name = splitData[0];

            double time = double.NaN;

            //Check for special values that indicate null values
            if (!Double.TryParse(splitData[3], out time))
            {
               continue;
            }

            if (time == -9999.25)
            {
               time = double.NaN;
            }

            form.Time = time;

            double depth = double.NaN;

            //Check for special values that indicate null values
            if (!Double.TryParse(splitData[1], out depth))
            {
               continue;
            }

            if (depth == -9999.25)
            {
               depth = double.NaN;
            }

            form.Depth = depth;

            Well.Tops.Formations.Add(form);
         }
      }

      public void ParseFile()
      {
         //Only parse once per filename
         if (Parsed)
            return;

         if (!File.Exists(Filename))
            throw new FileNotFoundException("Could not find file: " + Filename);

         string[] lines = File.ReadAllLines(Filename);

         if (lines.Length < 12)
            throw new FileLoadException("Error loading file. Invalid format.", Filename);

         Well = new Well();

         string wellname = "";

         //Find the well name
         int startIndex = FindWellname(lines, out wellname);

         if (startIndex == -1)
            throw new Exception();

         Well.Title = wellname;

         //Find the time datum
         Match match = _TimeDatumRegex.Match(lines[startIndex + 1]);

         double value = 0.0;

         if (!match.Success || !Double.TryParse(match.Groups["value"].Value.Trim(), out value))
         {
            throw new Exception();
         }

         Well.TimeDatum = value;

         //Find the KB
         match = _KBRegex.Match(lines[startIndex + 2]);

         if (!match.Success || !Double.TryParse(match.Groups["value"].Value.Trim(), out value))
         {
            throw new Exception();
         }

         Well.KB = value;

         //Determine the file type
         match = _SampleRegex.Match(lines[startIndex + 3]);

         if (match.Success)
         {
            //This is a log data file
            ParseLogDataFile(lines, startIndex);
         }
         else
         {
            //This is a formation tops file
            ParseTopsFile(lines, startIndex);
         }

         //List<string> headers = new List<string>(SeparateLine(lines[6]));



         ////Break up the data into doubles
         //List<List<double>> colData = new List<List<double>>();

         //for (int i = 0; i < headers.Count; i++)
         //{
         //   //Add one list per column
         //   colData.Add(new List<double>());
         //}

         //for (int i = 10; i < lines.Length; i++)
         //{
         //   string[] splitData = SeparateLine(lines[i]);

         //   if (splitData.Length != headers.Count)
         //      continue;

         //   for (int colIdx = 0; colIdx < splitData.Length; colIdx++)
         //   {
         //      double value = double.NaN;

         //      //Check for special values that indicate null values
         //      Double.TryParse(splitData[colIdx], out value);

         //      if (value == -9999.25)
         //      {
         //         value = double.NaN;
         //      }

         //      colData[colIdx].Add(value);
         //   }
         //}

         //List<double> rowData = null;

         ////find the independent column
         //for (int i = headers.Count - 1; i >= 0; i--)
         //{
         //   if (string.Equals("DEPTH", headers[i], StringComparison.OrdinalIgnoreCase))
         //   {
         //      rowData = colData[i];

         //      for (int j = 0; j < rowData.Count; j++)
         //      {
         //         rowData[j] *= -1.0;
         //      }

         //      colData.RemoveAt(i);
         //      headers.RemoveAt(i);
         //   }
         //   if (string.Equals("TIME", headers[i], StringComparison.OrdinalIgnoreCase))
         //   {
         //      colData.RemoveAt(i);
         //      headers.RemoveAt(i);
         //   }
         //}

         //int firstValid = 0;
         //int lastValid = 0;

         //FindLeadTrailZeros(rowData, out firstValid, out lastValid);

         //rowData.RemoveRange(lastValid + 1, rowData.Count - (lastValid + 1));
         //rowData.RemoveRange(0, firstValid);

         //for (int j = 0; j < colData.Count; j++)
         //{
         //   colData[j].RemoveRange(lastValid + 1, colData[j].Count - (lastValid + 1));
         //   colData[j].RemoveRange(0, firstValid);
         //}

         //RowData = rowData.ToArray();
         //RowCount = rowData.Count;
         //ColumnData = new double[colData.Count][];

         //for (int i = 0; i < colData.Count; i++)
         //{
         //   //Now find leading and trailing zeros and set to NaN
         //   FindLeadTrailZeros(colData[i], out firstValid, out lastValid);

         //   for (int j = 0; j < firstValid; j++)
         //   {
         //      colData[i][j] = double.NaN;
         //   }

         //   for (int j = lastValid + 1; j < colData[i].Count; j++)
         //   {
         //      colData[i][j] = double.NaN;
         //   }

         //   ColumnData[i] = colData[i].ToArray();
         //}

         //ColumnNames = headers.ToArray();

         ////Finally, try to set the default column name
         //SelectedColumn = ColumnNames[0];

         Parsed = true;
      }
   }
}
